package com.beemans.topon.demo.helper

import android.view.View
import androidx.annotation.IdRes
import androidx.lifecycle.LifecycleOwner
import com.beemans.common.ext.pt2px
import com.beemans.topon.ads.TpNativeAd
import com.beemans.topon.demo.constant.Constant
import com.beemans.topon.demo.ui.view.CustomNativeRender
import com.beemans.topon.kts.deepClone
import com.beemans.topon.views.NativeAdLayout
import com.chad.library.adapter.base.viewholder.BaseViewHolder

/**
 * @author ti
 * @date 2022/11/23.
 */
class NativeAdListHelper(
    private val owner: LifecycleOwner,
    @IdRes private val nativeAdLayoutId: Int,
    @IdRes private val selfRenderViewId: Int = -1,
) {
    private val holderMap by lazy { mutableMapOf<Int, BaseViewHolder>() }
    private val adLoaderMap by lazy { mutableMapOf<Int, TpNativeAd.TpNativeAdLoader>() }
    private val dislikeList by lazy { mutableListOf<Int>() }

    private val nativeAdConfig by lazy {
        TpNativeAd.TpNativeAdConfig(Constant.NATIVE_ID, 375.pt2px, 270.pt2px, isAutoLoadAd = false)
    }
    private var isAdLoading = false
    private var nativeAdLoader: TpNativeAd.TpNativeAdLoader? = null

    /**
     * 加载广告
     */
    fun loadAd(
        holder: BaseViewHolder,
        callback: (holder: BaseViewHolder, isVisible: Boolean) -> Unit,
    ) {
        if (isDislikeHolder(holder)) return
        val adLoader = getAdLoader(holder)
        if (adLoader != null) {
            reRenderAd(holder, adLoader, callback)
        } else {
            preloadAd(holder, callback)
        }
    }

    /**
     * 重新渲染广告
     */
    private fun reRenderAd(
        holder: BaseViewHolder,
        adLoader: TpNativeAd.TpNativeAdLoader,
        callback: (holder: BaseViewHolder, isVisible: Boolean) -> Unit,
    ) {
        callback.invoke(holder, true)
        val nativeAdLayout = holder.getView<NativeAdLayout>(nativeAdLayoutId)
        nativeAdLayout.setAdLoader(adLoader)
        adLoader.reRenderAd()
    }

    /**
     * 预加载广告
     */
    private fun preloadAd(
        holder: BaseViewHolder,
        callback: (holder: BaseViewHolder, isVisible: Boolean) -> Unit,
    ) {
        addHolder(holder)

        if (isAdLoading) return
        if (isAdReady()) {
            renderAd(callback)
            return
        }
        if (nativeAdLoader != null) {
            nativeAdLoader?.let(TpNativeAd::start)
            return
        }
        nativeAdConfig.deepClone()?.apply {
            isOnlyPreload = true
            isAutoLoadAd = true
        }?.let { config ->
            nativeAdLoader = TpNativeAd.TpNativeAdLoader(owner, config) {
                onAdStart {
                    isAdLoading = true
                }
                onAdLoaded {
                    isAdLoading = false
                    renderAd(callback)
                }
                onAdLoadFailed {
                    isAdLoading = false
                }
            }
        }
    }

    /**
     * 渲染广告
     */
    private fun renderAd(
        callback: (holder: BaseViewHolder, isVisible: Boolean) -> Unit,
    ) {
        val iterator = holderMap.iterator()
        while (iterator.hasNext()) {
            val holder = iterator.next().value
            if (!isAdReady()) {
                preloadAd(holder, callback)
                return
            }
            iterator.remove()

            val nativeAdLayout: NativeAdLayout = holder.getView(nativeAdLayoutId)

            var nativeSelfRender: CustomNativeRender? = null
            if (selfRenderViewId > 0) {
                val selfRenderView: View = holder.getView(selfRenderViewId)
                nativeSelfRender = CustomNativeRender(selfRenderView)
            }

            // 由于 item 复用，NativeAdLayout 持有旧的 TpNativeAdLoader 加载器
            // 所以需要先清除旧的 TpNativeAdLoader 加载器
            nativeAdLayout.setAdLoader(null)
            nativeAdLayout.loadAd(owner, nativeAdConfig, nativeSelfRender) {
                onAdStart { adLoader ->
                    callback.invoke(holder, true)
                    nativeAdLayout.setAdLoader(adLoader)
                    addAdLoader(holder, adLoader)
                }
                onAdClose {
                    callback.invoke(holder, false)
                    removeAdLoader(holder)
                    addDislikeHolder(holder)
                    true
                }
            }
        }
    }

    /**
     * 广告是否已就绪
     */
    private fun isAdReady(): Boolean {
        return nativeAdLoader?.checkValidAdCaches()?.isNotEmpty() == true
    }

    /**
     * 添加移除广告所对应的 [BaseViewHolder]
     */
    private fun addDislikeHolder(holder: BaseViewHolder) {
        val position = holder.layoutPosition
        if (!dislikeList.contains(position)) {
            dislikeList.add(position)
        }
    }

    /**
     * 判断 [BaseViewHolder] 所对应的视图是否被移除
     */
    private fun isDislikeHolder(holder: BaseViewHolder): Boolean {
        return dislikeList.contains(holder.layoutPosition)
    }

    /**
     * 添加 [BaseViewHolder] 所对应的广告加载器
     */
    private fun addAdLoader(holder: BaseViewHolder, adLoader: TpNativeAd.TpNativeAdLoader) {
        if (adLoaderMap.size >= LIMIT_AD_SIZE) {
            for ((key, _) in adLoaderMap) {
                adLoaderMap.remove(key)?.onAdDestroy()
                break
            }
        }
        adLoaderMap[holder.layoutPosition] = adLoader
    }

    /**
     * 移除 [BaseViewHolder] 所对应的广告加载器
     */
    private fun removeAdLoader(holder: BaseViewHolder) {
        adLoaderMap.remove(holder.layoutPosition)
    }

    /**
     * 获取 [BaseViewHolder] 所对应的广告加载器
     */
    private fun getAdLoader(holder: BaseViewHolder): TpNativeAd.TpNativeAdLoader? {
        return adLoaderMap[holder.layoutPosition]
    }

    /**
     * 添加所需要加载广告的 [BaseViewHolder]
     */
    private fun addHolder(holder: BaseViewHolder) {
        val position = holder.layoutPosition
        if (!holderMap.contains(position)) {
            holderMap[position] = holder
        }
    }

    /**
     * 移除 [BaseViewHolder]，不加载广告
     */
    fun removeHolder(holder: BaseViewHolder) {
        holderMap.remove(holder.layoutPosition)
    }

    companion object {
        private const val LIMIT_AD_SIZE = 20    //广告数量限制，防止内存溢出
    }
}